with ariane.maths.vector;
with ariane.maths.matrix.intf;

generic
    -- component type, which can be almost any type
  type component_t is private;

  -- number of rows or columns of the square matrix
  dimension : positive;

  -- a value of component type that represents zero
  zero : component_t;

  -- a value of component type that represents one
  one : component_t;

  -- operators defined on component type required by matrix operations
  -- duplicate formal subprogram names indicate that named association
  -- is not allowed and should use parameter passing in explicit order
  -- instead
  with function "+"(lhs, rhs: component_t) return component_t is <>;
  with function "-"(lhs, rhs: component_t) return component_t is <>;
  with function "*"(lhs, rhs: component_t) return component_t is <>;
  with function "/"(lhs, rhs: component_t) return component_t is <>;
  with function "-"(c : component_t) return component_t is <>;
  with function "<"(lhs, rhs: component_t) return boolean is <>;
  with function "abs"(c : component_t) return component_t is <>;

  with package vector is new ariane.maths.vector(component_t, dimension, zero);
  with package imatrix is new ariane.maths.matrix.intf(component_t);

package ariane.maths.matrix.square is


  -- the type of the array used to store the component values of the matrix
  type components_t is array (1..dimension, 1..dimension) of component_t;

  -- declaration of vector object provided to the user
  type object is new imatrix.object with private;

  -- creates an object and initializes it with specified data
  function create(components : components_t := (others=>(others=>zero)))
                  return object;

  -- constructor that initializes the object to be created with specified data
  -- as matrix type here is a constrained, we can use procedure-based
  -- initializer-styled constructor on an object defined unassigned
  -- the component array argument is optional which when absent is
  -- an array of default values
  procedure set(o : in out object;
                components : components_t := (others=>(others=>zero)));

  -- constructor of vector object
  -- the object is initialized as an identity matrix
  procedure identify(o : in out object);

  -- setter that assigns value to specific component
  procedure set(o : in out object; row, col : positive; val : component_t);

  -- getter that returns value of specific component
  function get(o : in object; row, col : positive) return component_t;

  -- returns the result of adding matrix lhs to rhs
  function "+"(lhs, rhs: object) return object;

  -- returns the result of subtracting matrix rhs from lhs
  function "-"(lhs, rhs: object) return object;

  -- returns the product of two matrices
  function "*"(lhs, rhs: object) return object;

  -- returns the negative of the given object
  function "-"(o : object) return object;

  -- returns the result of a matrix right multiplied by a vector
  function "*"(o : object; v : vector.object) return vector.object;

  -- returns the result of a matrix left multiplied by a vector
  function "*"(v : vector.object; o : object) return vector.object;

  -- turns the matrix to its inverse
  procedure invert(o : in out object; inverted : out boolean);

  -- returns the inverse of the give matrix
  function inverse(o : in object) return object;

  -- gets the number of rows in the matrix
  function numrows(o : in object) return positive;

  -- gets the number of colums in the matrix
  function numcols(o : in object) return positive;

private

  -- details of the data structure of the vector object
  type object is new imatrix.object with record
    components : components_t;
  end record;

end ariane.maths.matrix.square;
